﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using Linq2Crm.ObjectModel;
using Linq2Crm.ObjectModel.DomainObject;
using Linq2Crm.ObjectModel.DomainObject.Iternal;
using Linq2Crm.Request;
using SilverCrmSoap.CrmSdk;


namespace Linq2Crm
{
   public sealed partial class DomainObjectContext
   {
      private static Int32 isBusy;
      private Queue<CountedActionCallback> CompleteQueuePostback;
      private int operationCounter;
      
      private Queue<RequestBase> requestQueue = new Queue<RequestBase>();
      private Action callback;

      public static bool IsBusy
      {
         get { return isBusy == 1; }
         set
         {
            int val = value
                        ? 1
                        : 0;
            int oldValue = Interlocked.CompareExchange(ref isBusy, val, isBusy);
            if (oldValue != val)
            {
               // if async call just started and we are setting IsBusy to true, it's ok to make it immediately
               if (value == true)
               {
                  OnIsBusyChanged(EventArgs.Empty);
               }
               //if async call stoped and we are setting IsBusy to false, let's wait 100 msecs to not blink Loading screen. Maybee there will be further call after this one.
               else
               {
                  Task.Factory.StartNew(() =>
                  {
                     Thread.Sleep(100);
                     OnIsBusyChanged(EventArgs.Empty);
                  });
               }
            }
         }
      }

      public static event EventHandler IsBusyChanged;

      private static void OnIsBusyChanged(EventArgs e)
      {
         EventHandler handler = Interlocked.CompareExchange(ref IsBusyChanged, null, null);
         if (handler != null)
         {
            handler(null, e);
         }
      }

      private void IncrementCounter()
      {
         Interlocked.Increment(ref operationCounter);
         IsBusy = true;
      }

      private void DecrementCounter()
      {
         if (Interlocked.Decrement(ref operationCounter) == 0)
         {
            if (callback != null)
            {
               var call = callback;
               callback = null;
               call.Invoke();
            }
            if (requestQueue.Count > 0)
            {
               BeginExecuteQueueItem();
               return;
            }

            IsBusy = false;
         }
      }

      public void Execute(Action completedPostBack, params RequestBase[] requests)
      {
         if (requests.Length == 0)
         {
            return;
         }

         CompleteQueuePostback.Enqueue(new CountedActionCallback(completedPostBack, requests.Length + requestQueue.Count));
         foreach (RequestBase request in requests)
         {
            requestQueue.Enqueue(request);
         }
         BeginExecuteQueueItem();
      }

      public async Task ExecuteAsync(Action completedPostBack, params RequestBase[] requests)
      {
         if (requests.Length == 0)
         {
            return;
         }

         foreach (var requestBase in requests)
         {
            await requestBase.ExecuteAsync();
         }

         completedPostBack.Invoke();
      }

      public void Execute(RequestBase request)
      {
         if (request == null)
         {
            return;
         }

         requestQueue.Enqueue(request);

         BeginExecuteQueueItem();
      }

      public async Task<ResponseBase> ExecuteAsync(RequestBase request)
      {
         if (request == null)
         {
            return null;
         }

         var response = await request.ExecuteAsync();

         return response;
      }

      private void BeginExecuteQueueItem()
      {
         if (Interlocked.CompareExchange(ref operationCounter, operationCounter, 0) == 0)
         {
            if (CompleteQueuePostback.Count > 0)
            {
               if (--CompleteQueuePostback.Peek().RequestLenght == 0)
               {
                  callback = CompleteQueuePostback.Dequeue().Callback;
               }
            }

            RequestBase request = requestQueue.Dequeue();

            request.Execute();
         }
      }

      public IOrganizationService GetService()
      {
         return OrganizationService;
      }

      public Dictionary<RecordStateCode, Dictionary<int, string>> GetStatuses<T>(int lcid = 0, Action<Dictionary<RecordStateCode, Dictionary<int, string>>, Exception> asyncCallback = null)
         where T : IDomainObject
      {
         AttributeParser ap = new AttributeParser(typeof(T));
         AnalyzeResult analyze = ap.AnalyzeType();
         if (_attributesList.ContainsKey(analyze.EntityName))
         {
            DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyze.EntityName].Attributes);
            return domF.GetStatusList(lcid);
         }
         ExecuteWithAttributeRequest(
            exception =>
            {
               if (exception != null)
               {
                  if (asyncCallback != null)
                  {
                     asyncCallback(null, exception);
                  }
               }
               else
               {
                  DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyze.EntityName].Attributes);
                  if (asyncCallback != null)
                  {
                     asyncCallback(domF.GetStatusList(lcid), null);
                  }
               }
            }, analyze.EntityName);
         return null;
      }

      public async Task<Tuple<Dictionary<RecordStateCode, Dictionary<int, string>>, Exception>> GetStatusesAsync<T>(int lcid = 0)
         where T : IDomainObject
      {
         AttributeParser ap = new AttributeParser(typeof(T));
         AnalyzeResult analyze = ap.AnalyzeType();
         var ex = await ExecuteWithAttributeRequestAsync(analyze.EntityName);
         if (ex != null)
         {
            return new Tuple<Dictionary<RecordStateCode, Dictionary<int, string>>, Exception>(new Dictionary<RecordStateCode, Dictionary<int, string>>(), ex);
         }
         DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyze.EntityName].Attributes);
         return new Tuple<Dictionary<RecordStateCode, Dictionary<int, string>>, Exception>(domF.GetStatusList(lcid), null);
      }

      public async Task<Tuple<Dictionary<int, string>, Exception>> GetOptionSet<T>(string propertySchemaName)
         where T : IDomainObject
      {
         AttributeParser ap = new AttributeParser(typeof(T));
         AnalyzeResult analyze = ap.AnalyzeType();
         var ex = await ExecuteWithAttributeRequestAsync(analyze.EntityName);
         if (ex != null)
         {
            return new Tuple<Dictionary<int, string>, Exception>(new Dictionary<int, string>(), ex);
         }
         DomainObjectFactory domF = new DomainObjectFactory(_attributesList[analyze.EntityName].Attributes);
         return new Tuple<Dictionary<int, string>, Exception>(domF.GetOptionSet(propertySchemaName), null);
      }

//      /// <summary>
//      /// Gets default ViewId if specified in UserSettings for specific entity
//      /// </summary>
//      /// <typeparam name="T">Entity for defining EntityTypeCode</typeparam>
//      /// <param name="userSettings">User settings that are returned from WhoAmIResponse</param>
//      /// <returns>Guid = Empty if default not specified; Exception if occurs</returns>
//      public async Task<Tuple<Guid, Exception>> GetDefaultViewIdByUserSettings<T>(Dictionary<string, object> userSettings)
//         where T : IDomainObject
//      {
//         AttributeParser ap = new AttributeParser(typeof(T));
//         AnalyzeResult analyze = ap.AnalyzeType();
//         var ex = await ExecuteWithAttributeRequestAsync(analyze.EntityName);
//         if (ex != null)
//         {
//            return new Tuple<Guid, Exception>(Guid.Empty, ex);
//         }
//         
//         if (!userSettings.ContainsKey("personalizationsettings") || string.IsNullOrEmpty(userSettings["personalizationsettings"] as string))
//         {
//            return new Tuple<Guid, Exception>(Guid.Empty, null);
//         }
//         System.Runtime.Serialization.DataContractSerializer serializer = new DataContractSerializer(typeof(List<DefaultGridView>), "DefaultGridViews", "");
//
//
//         byte[] buffer = System.Text.Encoding.Unicode.GetBytes((string) userSettings["personalizationsettings"]);
//         List<DefaultGridView> defaultViews = null;
//         try
//         {
//            using (MemoryStream ms = new MemoryStream())
//            {
//               ms.Write(buffer, 0, buffer.Length);
//               ms.Position = 0;
//
//               XmlReader read = XmlReader.Create(ms, new XmlReaderSettings(), "");
//               defaultViews = (List<DefaultGridView>) serializer.ReadObject(read);
//            }
//         }
//         catch (Exception e)
//         {
//            return new Tuple<Guid, Exception>(Guid.Empty, e);
//         }
//
//         var defaultForEntity = defaultViews.FirstOrDefault(x => x.EntityTypeCode == _attributesList[analyze.EntityName].ObjectTypeCode);
//         if (defaultForEntity == null)
//         {
//            return new Tuple<Guid, Exception>(Guid.Empty, null);
//         }
//         return new Tuple<Guid, Exception>(defaultForEntity.ViewGuid, null);
//      }
   }
}